package ru.urfc.portal.ui.client.widgets;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import ru.urfc.portal.ui.client.Agregator;
import ru.urfc.portal.ui.client.BaseDocument;
import ru.urfc.portal.ui.client.BaseGooglet;
import ru.urfc.portal.ui.client.Constants;
import ru.urfc.portal.ui.client.Utils;
import ru.urfc.portal.ui.client.controllers.GridController;
import ru.urfc.portal.ui.client.controllers.ItemsController;
import ru.urfc.portal.ui.client.controllers.events.GetEvent;
import ru.urfc.portal.ui.client.descriptor.GridDescriptor;
import ru.urfc.portal.ui.client.descriptor.GridField;
import ru.urfc.portal.ui.client.googlets.client.DeviceList;

import com.extjs.gxt.ui.client.data.LoadEvent;
import com.extjs.gxt.ui.client.data.Loader;
import com.extjs.gxt.ui.client.data.ModelData;
import com.extjs.gxt.ui.client.event.BaseEvent;
import com.extjs.gxt.ui.client.event.BaseObservable;
import com.extjs.gxt.ui.client.event.EventType;
import com.extjs.gxt.ui.client.event.Listener;
import com.extjs.gxt.ui.client.event.LoadListener;
import com.extjs.gxt.ui.client.event.Observable;
import com.extjs.gxt.ui.client.store.ListStore;
import com.extjs.gxt.ui.client.widget.grid.CheckBoxSelectionModel;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.event.shared.GwtEvent.Type;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Widget;

/**
 * TODO make refactoring
 */
public class SelectDevices extends Composite implements Constants {

	interface Binder extends UiBinder<Widget, SelectDevices> {}
	private static Binder binder = GWT.create(Binder.class);
	
	private Observable observable = new BaseObservable();
	private HandlerManager eventsDispatcher;
	
	String idsAsStr;
	GridField idField;
	
	@UiField Element mainView;
	@UiField Element selectView;
	
	@UiField MegaGrid mainDevices;
	@UiField MegaGrid selectDevices;
	@UiField DeviceFilter selectDevicesFilter;
	Map<String, ModelData> idToDevice = new HashMap<String, ModelData>();  
	
	@UiField Button search;
	
	static public EventType DEVICES_SELECTED = new EventType(); 
	public Type<GridController> SEARCH = new Type<GridController>();
	
	
	public SelectDevices() {
		initWidget(binder.createAndBindUi(this));
	}
	
	SelectDevices(boolean test) {		
	}
	
	@UiHandler("showSelect") 
	public void showSelectDevices(ClickEvent e) {
		Utils.hide(mainView);
		Utils.show(selectView);
		selectDevices.getView().refresh(true);// columns widths recalculating
	}

	@UiHandler("select") 
	public void selectDevices(ClickEvent e) {
		Utils.show(mainView);
		Utils.hide(selectView);
		observable.fireEvent(DEVICES_SELECTED, new SelectedDevicesEvent(calculateIds()));
	}

	@UiHandler("search")
	void search(ClickEvent e) {
		idsAsStr = calculateIds();
		eventsDispatcher.fireEvent(new GetEvent(SEARCH));
	}

	private String calculateIds() {
		List<ModelData> changedItems = selectDevices.getStore().getModels();
		List<ModelData> selectedItems = selectDevices.getSelectionModel().getSelection();
		for (ModelData item: changedItems) {
			idToDevice.remove(idField.getValue(item));
		}
		for (ModelData item: selectedItems) {
			idToDevice.put((String)idField.getValue(item), item);
		}
		return doSelect(idsAsStr, idField, changedItems, selectedItems);
	}	
	
	String doSelect(String ids, GridField idField, List<ModelData> changedItems, List<ModelData> selectedItems) {
		List<String> idsList = new ArrayList<String>();
		if (!(ids == null || ids.length() == 0)) {
			idsList.addAll(Arrays.asList(ids.split(DOCUMENT_IDS_SEPARATOR)));
		}
		for (ModelData modelData: changedItems) {
			String id = idField.getValue(modelData);
			idsList.remove(id);
		}
		for (ModelData modelData: selectedItems) {
			String id = idField.getValue(modelData);
			idsList.add(id);
		}
		return Utils.toString(idsList, DOCUMENT_IDS_SEPARATOR);
	}

	public void init(BaseDocument document, String clientId, String documentId) {
		initMainDevices(document, clientId, documentId);
		
		GridDescriptor descriptor = createDescriptor(selectDevices, DEVICE_WITH_NET, CLIENTS, clientId, DEVICES);
		// Add CheckBox column
		CheckBoxSelectionModel<ModelData> selectionModel = new CheckBoxSelectionModel<ModelData>();
		descriptor.getColumnModel().getColumns().add(0, selectionModel.getColumn());
		selectDevices.setSelectionModel(selectionModel);

		GridController controller = document.createController(descriptor, selectDevices);
		initFilter(document, controller);

		selectDevices.getStore().getLoader().addListener(Loader.Load, new LoadListener() {
			@Override
			public void loaderLoad(LoadEvent le) {
				markSelected();
			}
		});
		createAgregator(document.getController(), document.getIdsField(), selectDevices.getStore());
		createDevicesSelectedListener(document.getController(), document.getIdsField());
	}
	
	private void initMainDevices(BaseGooglet googlet, String clientId, String documentId) {
		GridDescriptor descriptor = createDescriptor(mainDevices, DEVICE_WITH_NET,
				CLIENTS, clientId, DOCUMENTS, documentId, DOCUMENT_SELECTED_DEVICES);
		googlet.createController(descriptor, mainDevices);
	}
	
	private GridDescriptor createDescriptor(MegaGrid grid, String recordName, String... restUrl) {
		GridDescriptor descriptor = new GridDescriptor(recordName, restUrl);
		idField = descriptor.addField("device/id");	// Add field to identify selected items  
		GridField autoExpandColumnField = DeviceList.createColumns(descriptor);
		grid.setAutoExpandColumn(autoExpandColumnField.getName());
		return descriptor;
	}

	private void initFilter(BaseGooglet googlet, GridController controller) {
		eventsDispatcher = googlet.getEventsDispatcher();
		selectDevicesFilter.setupButton(search.getElement());
		selectDevicesFilter.init(googlet);
		googlet.register(SEARCH, controller).setFilter(selectDevicesFilter);
	}

	private void createAgregator(final ItemsController documentController, 
			final GridField idsField, ListStore<ModelData> store) {
		Agregator agregator = new Agregator();
		agregator.listenLoaded(store);
		agregator.listenLoaded(documentController.getLoader());
		agregator.addListener(Agregator.AGREGATED, new Listener<BaseEvent>() {
			@Override
			public void handleEvent(BaseEvent be) {
				// Select items with id's
				ModelData documentModelData = documentController.getModelData();
				idsAsStr = idsField.getValue(documentModelData);
				markSelected();
			}
		});
	}

	private void markSelected() {
		for (String id: getIds(idsAsStr)) {
			for (ModelData item: selectDevices.getStore().getModels()) {
				if (idField.getValue(item).equals(id)) {
					selectDevices.getSelectionModel().select(true, item);
				}
			}
		}
	}

	private String[] getIds(String devicesIds) {
		if (devicesIds == null || devicesIds.length() == 0) {
			return new String[]{};
		}
		return devicesIds.split(DOCUMENT_IDS_SEPARATOR);
	}

	public void addListener(EventType eventType, Listener<SelectedDevicesEvent> listener) {
		observable.addListener(eventType, listener);
	}	

	private void createDevicesSelectedListener(final ItemsController docController, final GridField idsField) {
		addListener(SelectDevices.DEVICES_SELECTED, new Listener<SelectedDevicesEvent>() {
			@Override
			public void handleEvent(SelectedDevicesEvent event) {
				idsField.setValue(docController.getModelData(), event.getDevicesIds());
				List<ModelData> list = new ArrayList<ModelData>(mainDevices.getStore().getModels());
				for (ModelData item: list) {
					mainDevices.getStore().remove(item);
				}
				for (String id: getIds(event.getDevicesIds())) {
					ModelData item = idToDevice.get(id);
					mainDevices.getStore().add(item);
				}
				mainDevices.getColumnModel();
			}
		});
	}
}